package club.jdiy.dev.controller;

import club.jdiy.admin.interceptor.GuestDisabled;
import club.jdiy.core.base.domain.Ret;
import club.jdiy.core.AppContext;
import club.jdiy.core.sql.Rs;
import club.jdiy.core.sql.TableInfo;
import club.jdiy.core.storage.Store;
import club.jdiy.utils.ArrayUtils;
import club.jdiy.utils.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * JDiy 管理员后台 附件处理 通用Controller
 *
 * @author ziquee(子秋) QQ号:39886616 主页: http://www.jdiy.club
 * @version 20210629
 */
@Controller
@RequestMapping("mgmt/JDiy")
public class JDiyStoreController implements JDiyController {

    /**
     * 后台附件上传通用保存方法.
     * 禁止将@RequestMapping地址暴露至前台．此地址只允许已登录后台的授权用户访问．
     *
     * @param id     附件所属的实体主键　(需要在dir主目录中具有唯一性)
     * @param field  附件字段名称 (可以向同一个附件字段存储一个或多个文件)
     * @param file   上传的文件MultiprtFile对象
     * @param append 是否为追加上传模式(即允许向同一个附件字段上传多个附件), 默认为true
     * @param dir    存储主目录名（默认为entity）.  若实体表的主键为自增（或不具备全库唯一特性），建议dir传入值为数据库的表名
     * @return 该附件字段已上传的文件Url全路径列表．
     */
    @GuestDisabled
    @RequestMapping("store.upload")
    @ResponseBody
    public Ret<String[]> upload(
            String id,
            String field,
            MultipartFile file,
            @RequestParam(defaultValue = "true") Boolean append,
            @RequestParam(defaultValue = "entity") String dir) {
        try {
            Store store = context.getStore(dir, id);
            if (Boolean.TRUE.equals(append)) {
                store.add(field, file);
            } else {
                store.set(field, file);
            }
            return Ret.success(store.get(field).getUrls());
        } catch (Exception e) {
            return Ret.error(e);
        }
    }

    /**
     * 后台相应页面中管理Store附件：列出已上传的附件条目.
     * 禁止将@RequestMapping地址暴露至前台．此地址只允许已登录后台的授权用户访问．
     *
     * @param id       附件所属的实体主键　(需要在dir主目录中具有唯一性)
     * @param field    附件字段名称 (可以向同一个附件字段存储一个或多个文件)
     * @param showType 显示类型。 值为image时，以小缩图显示，否则显示为文件链接
     * @param canDel   是否允许删除附件
     * @param dir      存储主目录名（默认为entity）. 通常与表名一致
     * @return ret
     */
    @RequestMapping("store.list")
    public String storeList(String id, String field,
                            @RequestParam(defaultValue = "file") String showType, //link | image
                            @RequestParam(defaultValue = "false") Boolean canDel,
                            @RequestParam(defaultValue = "entity") String dir,  //bucket
                            @RequestParam(defaultValue = "") String table,  //若指定此参数，删除store字段时，同步更新数据库字段（即竖线分隔的数据库字段本身）
                            ModelMap map) {
        map.put("store", context.getStore(dir, id));
        map.put("showType", showType);
        map.put("dir", dir);
        map.put("table", table);
        map.put("field", field);
        map.put("id", id);
        map.put("canDel", Boolean.TRUE.equals(canDel));
        return "store.ls";
    }

    /**
     * 后台相应页面中上传附件列出显示（不写Store模式。即附件url直接以竖线分隔存入到表字段中）
     *
     * @param map
     * @param canDel   是否允许删除附件
     * @param showType 显示类型。 值为image时，以小缩图显示，否则显示为文件链接
     * @param table    目标数据表名
     * @param id       目录记录主键
     * @param field    目标表的附件字段名
     * @return ret
     */
    @RequestMapping("store.nls")
    public String noStoreList(ModelMap map,
                              @RequestParam(defaultValue = "false") Boolean canDel,
                              @RequestParam(defaultValue = "file") String showType, //link | image
                              String table, String id, String field) {
        map.put("canDel", Boolean.TRUE.equals(canDel));
        map.put("showType", showType);
        map.put("table", table);
        map.put("id", id);
        map.put("field", field);
        try {
            Rs rs = context.getDao().rs(table, id, field);
            if (!rs.isNew() && rs.get(field) != null) {
                List<Map<String, String>> attachList = new ArrayList<>();
                for (String s : rs.getString(field).split("\\|")) {
                    if (s != null && !"".equals(s.trim())) {
                        Map<String, String> it = new HashMap<>();
                        it.put("url", s.trim());
                        it.put("ext", s.substring(s.lastIndexOf(".") + 1).trim().toLowerCase());
                        it.put("name", s.substring(s.lastIndexOf("/") + 1).trim());
                        attachList.add(it);
                    }
                }
                if (!attachList.isEmpty()) map.put("attachList", attachList);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "store.nls";
    }

    /**
     * 后台附件上传通用删除方法.
     * 禁止将@RequestMapping地址暴露至前台．此地址只允许已登录后台的授权用户访问．
     *
     * @param id    附件所属的实体主键　(需要在dir主目录中具有唯一性)
     * @param field 附件字段名称 (可以向同一个附件字段存储一个或多个文件)
     * @param key   要删除的附件key
     * @param dir   存储主目录名（默认为entity）. 与上传时指定的主目录一致
     * @return ret
     */
    @GuestDisabled
    @RequestMapping("store.delete")
    @ResponseBody
    public Ret<?> storeDelete(String id, String field, String key,
                              @RequestParam(defaultValue = "entity") String dir,
                              @RequestParam(defaultValue = "") String table
    ) {
        try {
            Store store = context.getStore(dir, id);
            store.del(field, key);

            //********** 若指定了table参数，同步更新数据表的附件字段（竖线分隔）
            try {
                if (StringUtils.hasText(table)) {
                    TableInfo tableInfo = context.getDao().getTableInfo(table);
                    if(tableInfo.getColumns().containsKey(field)) {
                        Rs rs = context.getDao().rs(table, id, tableInfo.getPrimaryKey() + "," + field);
                        if (!rs.isNew()) {
                            rs.set(field, ArrayUtils.join(store.get(field).getUrls(), "\\|"));
                            context.getDao().save(rs);
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            //************************************************************

            return Ret.success();
        } catch (Exception ex) {
            return Ret.error(ex);
        }
    }

    /**
     * 后台数据表字段附件(多个附件url竖线分隔)通用删除方法.
     * 系统从目标表的字段中移除指定的附件url
     *
     * @param table 目标数据表名
     * @param id    目标数据主键
     * @param field 目标字段
     * @param url   附件URL地址
     * @return ret
     */
    @GuestDisabled
    @RequestMapping("noStore.delete")
    @ResponseBody
    public Ret<?> noStoreDelete(String table, String id, String field, String url) {
        try {
            TableInfo tableInfo = context.getDao().getTableInfo(table);
            Rs rs = context.getDao().rs(table, id, tableInfo.getPrimaryKey() + "," + field);
            if (!rs.isNew() && rs.get(field) != null) {
                String[] urls = rs.getString(field).split("\\|");
                List<String> ls = new ArrayList<>();
                for (String s : urls) {
                    if (StringUtils.hasText(s) && !s.trim().equals(url)) ls.add(s);
                }
                context.getDao().save(rs.set(field, StringUtils.join(ls, "|")));
            }
            return Ret.success();
        } catch (Exception ex) {
            return Ret.error(ex);
        }
    }

    @Resource
    private AppContext context;
}
